<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">
  <meta name="google-site-verification" content="d44tDfSSWxm1_XP1dAq65hkgyD6zw70Ua9JdCaJqWGg">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"zasdfgbnm.github.io","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="In recent years, docker has created a containerization boom around the world by providing a way to easily create and run application containers. Containers save people from dependency hell by packagin">
<meta property="og:type" content="article">
<meta property="og:title" content="Using docker image as your desktop system">
<meta property="og:url" content="https://zasdfgbnm.github.io/2017/12/29/Using-docker-image-as-your-desktop-system/index.html">
<meta property="og:site_name" content="zasdfgbnm">
<meta property="og:description" content="In recent years, docker has created a containerization boom around the world by providing a way to easily create and run application containers. Containers save people from dependency hell by packagin">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2017-12-30T01:08:54.000Z">
<meta property="article:modified_time" content="2021-04-04T05:17:59.766Z">
<meta property="article:author" content="zasdfgbnm">
<meta property="article:tag" content="Linux">
<meta property="article:tag" content="docker">
<meta property="article:tag" content="initramfs">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://zasdfgbnm.github.io/2017/12/29/Using-docker-image-as-your-desktop-system/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'en'
  };
</script>

  <title>Using docker image as your desktop system | zasdfgbnm</title>
  
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-7583294-5"></script>
    <script>
      if (CONFIG.hostname === location.hostname) {
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-7583294-5');
      }
    </script>


  <script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?a56abdeb557a286a6b7a104348fdfbcd";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/atom.xml" title="zasdfgbnm" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">zasdfgbnm</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>Home</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>Archives</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>About</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>Tags</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>Categories</a>

  </li>
        <li class="menu-item menu-item-sitemap">

    <a href="/sitemap.xml" rel="section"><i class="fa fa-sitemap fa-fw"></i>Sitemap</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
    <link itemprop="mainEntityOfPage" href="https://zasdfgbnm.github.io/2017/12/29/Using-docker-image-as-your-desktop-system/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="zasdfgbnm">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="zasdfgbnm">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          Using docker image as your desktop system
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2017-12-29 20:08:54" itemprop="dateCreated datePublished" datetime="2017-12-29T20:08:54-05:00">2017-12-29</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2021-04-04 01:17:59" itemprop="dateModified" datetime="2021-04-04T01:17:59-04:00">2021-04-04</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">In</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a>
                </span>
            </span>

          
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="far fa-comment"></i>
      </span>
      <span class="post-meta-item-text">Disqus: </span>
    
    <a title="disqus" href="/2017/12/29/Using-docker-image-as-your-desktop-system/#disqus_thread" itemprop="discussionUrl">
      <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017/12/29/Using-docker-image-as-your-desktop-system/" itemprop="commentCount"></span>
    </a>
  </span>
  
  

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p>In recent years, docker has created a containerization boom around the world by providing a way to easily create and run application containers. Containers save people from dependency hell by packaging the software with the operating environment it needs. Although docker was designed to be neither an operating system container nor an operating system running directly on the bare metal, docker’s powerful suite of tools will also give us tremendous convenience in managing our desktop system running on bare metal.</p>
<p>Why using docker image as a desktop system is a good idea? Let’s begin with talking about the inconvenience of the normal way how people are managing their desktop systems. Nowadays, most of us has more than one computer, and we want these computers to be “consistent”. Here when I say “consistent”, I mean, for example, I begin writing a document on one computer (say, at home) and am unable to finish it before having to switch to another computer (say, at work). I don’t want to worry about copying it manually to another computer, instead, I want it to be able to magically appear there so I can access it at any time. This is exactly what cloud sync disks like Dropbox do for us.  However, for geeks, what cloud sync disks do is far from enough. For example, you are busy with a project,  which uses a number of programming languages, libraries, and a bunch of  GUI and non-GUI tools. As you keep trying new things, you install new tools and change configurations continually on your system. It would be nice if these changes can be synced across different devices automatically so that when you install something you won’t need to install it one by one on each of your computers.</p>
<span id="more"></span>

<h1 id="The-philosophy-of-docker"><a href="#The-philosophy-of-docker" class="headerlink" title="The philosophy of docker"></a>The philosophy of docker</h1><p>Readers unfamiliar with docker can check out <a target="_blank" rel="noopener" href="https://docs.docker.com/get-started/">the official tutorial at here</a>. Docker is easy to use: we start by writing a Dockerfile containing commands that install and configure the libraries and tools we want. Readers unfamiliar with docker can take a look at the Dockerfile example below <a target="_blank" rel="noopener" href="https://github.com/kstaken/dockerfile-examples/blob/master/nodejs/Dockerfile">that I borrow</a> to get a quick idea on how a Dockerfile looks like:</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> ubuntu</span><br><span class="line"><span class="keyword">MAINTAINER</span> Kimbro Staken</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get install -y software-properties-common python</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> add-apt-repository ppa:chris-lea/node.js</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> <span class="built_in">echo</span> <span class="string">&quot;deb http://us.archive.ubuntu.com/ubuntu/ precise universe&quot;</span> &gt;&gt; /etc/apt/sources.list</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get update</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apt-get install -y nodejs</span></span><br><span class="line"><span class="comment">#RUN apt-get install -y nodejs=0.6.12~dfsg1-1ubuntu1</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> mkdir /var/www</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> app.js /var/www/app.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">&quot;/usr/bin/node&quot;</span>, <span class="string">&quot;/var/www/app.js&quot;</span>] </span></span><br></pre></td></tr></table></figure>

<p>With a Dockerfile, a docker image can be created with just a single <code>docker build</code> command. The Docker company offers a service called DockerHub that can host public images for free. Use <code>docker push</code> to upload the image to DockerHub. To get the latest version of your image from DockerHub from a different computer, you only need a <code>docker pull</code>. DockerHub also supports auto-build. By connecting your DockerHub account with your GitHub account, you can make DockerHub automatically regenerates the image whenever the Dockerfile on GitHub changes.</p>
<p>Docker is actually giving us the solution to the maintenance of consistency mentioned at the beginning of the article: we create a docker image that has everything we needed for our project. In that case, everything related to this project, such as development, testing, deployment, etc., can be done by first opening a container with <code>docker run</code> and then doing all the work inside. When we want to install something new or change some settings, we simply make the appropriate changes in the Dockerfile, rebuild the image, and then update it with <code>docker pull</code> on all the machines using it. This philosophy of use provides a very elegant solution to the problem of consistency through a centralized repository. The only drawback is that not all programs can run in the container, and not all of the programs are easy to run in the container. If you are using a GUI program, or some system-level program, then using the programs inside a container can be a lot of hassle.  So, to overcome this drawback, it is natural to ask: can we mount a docker image directly as the root directory when we boot, so that we can run the image on the bare machine and use it as our daily desktop system?</p>
<p>There are other benefits to this approach other than the convenience of maintaining consistency:</p>
<ul>
<li>The entire system is stored in the cloud, the local content is only a cache of the cloud, so there is no need for regular backup of the system.</li>
<li>How your system is configured from scratch to the way you want is clearly written in the Dockerfile. Dockerfile then becomes your best note.</li>
<li>No need to worry about junk files or data corruption of some programs after using your system for a long time. Because every time you turn your computer on, you are using a brand new system.</li>
<li>When you get a new machine, you don’t have to install the operating system from scratch: just pull the image from DockerHub.</li>
<li>The process of a system update is nothing but rebuilding a Dockerfile from the latest software repository. Less human intervention will be needed compared to the normal system upgrade, which sometimes encounters file conflicts or dependency problems.</li>
</ul>
<h1 id="Docker-storage-driver"><a href="#Docker-storage-driver" class="headerlink" title="Docker storage driver"></a>Docker storage driver</h1><p>There is <a target="_blank" rel="noopener" href="https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/">an introduction to docker’s storage driver on the official site</a>, so we only discuss it briefly here. Docker uses the concept of layers. When building the image, docker executes the Dockerfile line by line and a new layer is created when executing each line. Only the diff is stored for each layer. When we do a <code>docker pull</code> or <code>docker push</code>, what docker actually does is download or upload the delta between layers instead of the whole layer. Whenever we do a <code>docker run</code>, docker will stack these downloaded deltas together to get a complete image. A new read-write layer (we will call it rwlayer in later contexts) on the top would also be created so that all writes to the container go to the rwlayer and the image itself is kept read-only. The concept of “layer” is actually implemented differently for different file systems where the docker directory (usually the directory <code>/var/lib/docker</code> on your computer) resides. These implementations are called graph drivers. Build-in graph drivers include aufs, overlay, btrfs, zfs, devicemapper and so on. Most graph drivers use copy-on-write techniques so that the process of creating a new layer does not require making a new copy of the data. The actual copy takes place when a write happens.</p>
<p>As the author only uses btrfs, this article will focus on btrfs. Btrfs is a copy-on-write system. To make use of the copy-on-write feature of btrfs, whenever stacking a new layer, docker creates a snapshot of the original layer’s subvolume and write the diff into the new snapshot. When creating a container from an image, docker would create a snapshot of the top layer’s subvolume and use it as the rwlayer.</p>
<h1 id="Boot-to-a-docker-image"><a href="#Boot-to-a-docker-image" class="headerlink" title="Boot to a docker image"></a>Boot to a docker image</h1><p>Besides the knowledge on how docker storage drivers work just discussed, we still need to know the Linux startup process to achieve our goal. During boot, the boot manager would load the kernel and a ramdisk called initramfs into memory. After some very basic initializations, the kernel will extract the initramfs to the root directory <code>/</code> and then start the init program in the memory disk (usually <code>/ init</code>). This init program will do some further initialization (for example, loading the file system drivers, do fsck, etc.). Then, this init program will mount the real root directory based on the kernel options <code>root</code>, <code>rootflags</code>, etc. and uses the <code>switch_root</code> program to switch the <code>/</code> from the initramfs to the mounted new root. The init program will be started by <code>switch_root</code> to make final initializations, such as mounting entries in fstab, loading graphics interface and so on. Many distributions provide tools to generate initramfs.  These tools are often modular and allow users to add their own hooks. The Arch Linux’s such tool is called “mkinitcpio”.</p>
<p>Now we have all the knowledge needed to boot into a docker image, and here is the idea: we can add a hook to initramfs, which creates a snapshot from the desired image’s subvolume in docker’s local cache as a rwlayer before the new root is mounted. The real root is set to be the rwlayer, which will then be mounted and <code>switch_root</code>ed by the init in initramfs. In detail, when writing the kernel option in boot manager’s config file, <code>root</code> should be the partition where your <code>/var/ lib/docker</code> is located, and <code>rootflags</code> must have a <code>subvol=XXXXX</code>, where XXXXX is the relative location of the rwlayer that we plan to create with respect to <code>root</code>. The most important thing to do is to write a hook that: find the btrfs subvolume for the desired docker image, and then create a snapshot of that subvolume named XXXXX (the same name as in the kernel option). If you are using Arch Linux like the author, then all of the works have been done and the reader can use the author’s hook at GitHub directly.</p>
<p>The code is located at: <a target="_blank" rel="noopener" href="https://github.com/zasdfgbnm/mkinitcpio-docker-hooks">https://github.com/zasdfgbnm/mkinitcpio-docker-hooks</a> .  Besides, the readers can also install <code>mkinitcpio-docker-hooks</code> directly from AUR.  A step-by-step tutorial to use this hook is given in the following section.</p>
<h1 id="Make-a-docker-image-your-desktop-system-using-mkinitcpio-docker-hooks"><a href="#Make-a-docker-image-your-desktop-system-using-mkinitcpio-docker-hooks" class="headerlink" title="Make a docker image your desktop system using mkinitcpio-docker-hooks"></a>Make a docker image your desktop system using mkinitcpio-docker-hooks</h1><p>The usage of mkinitcpio-docker-hooks is roughly divided into the following steps:</p>
<ol>
<li>Make sure your <code>/var/lib/docker</code> is in a btrfs partition</li>
<li>Prepare a docker image suitable for booting on bare metal</li>
<li>Install and configure mkinitcpio-docker-hooks in this docker image</li>
<li>Prepare the kernel and initramfs</li>
<li>Prepare the top layer content</li>
<li>Setup boot manager</li>
</ol>
<h2 id="Prepare-a-docker-image"><a href="#Prepare-a-docker-image" class="headerlink" title="Prepare a docker image"></a>Prepare a docker image</h2><p>To run a docker image on bare metal, you first need to have a docker image that is suitable for doing so. Many docker images, in order to reduce the size of the image, do not come with software packages that are only useful to bare metal running (such as dhcpcd).  So we need to manually install these packages in Dockerfile. For Arch Linux, this can be done simply by installing the group named <code>base</code>. </p>
<p>Since the next step is to install mkinitcpio-docker-hooks, it is recommended to base your docker image from an image that comes with yaourt. Personally, I use <a target="_blank" rel="noopener" href="https://cloud.docker.com/swarm/zasdfgbnm/repository/docker/zasdfgbnm/archlinux-yaourt/general">my own archlinux-yaourt image named <code>zasdfgbnm/archlinux-yaourt</code></a>. Therefore, the first few lines of Dockerfile looks like this:</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> zasdfgbnm/archlinux-yaourt</span><br><span class="line"><span class="keyword">USER</span> root</span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> pacman -Syu --noconfirm base</span></span><br></pre></td></tr></table></figure>

<p>Since this is just a demo, I will not install any other package here. The readers may want to install other packages according to their needs.</p>
<h2 id="Installation-and-configuration-of-mkinitcpio-docker-hooks"><a href="#Installation-and-configuration-of-mkinitcpio-docker-hooks" class="headerlink" title="Installation and configuration of mkinitcpio-docker-hooks"></a>Installation and configuration of mkinitcpio-docker-hooks</h2><p>The mkinitcpio-docker-hooks package should be installed inside the docker image that we plan to use as our desktop. The reason is mainly that the Linux kernel does not provide ABI stability. Therefore, the kernel modules must be strictly consistent the kernel version, otherwise, the modules cannot be loaded. In order to maintain this consistency, what we do is to install mkinitcpio-docker-hooks and generate initramfs in docker, and use the kernel from the docker image to boot. Doing so will ensure that the kernel, modules copied to initramfs, and the <code>/lib/modules</code> inside the docker image are for the same kernel. To install mkinitcpio-docker-hooks, add the following to Dockerfile:</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">RUN</span><span class="bash"> sudo -u user yaourt -S --noconfirm mkinitcpio-docker-hooks</span></span><br></pre></td></tr></table></figure>

<p>The configuration file for mkinitcpio-docker-hooks is <code>/etc/docker-btrfs.json</code>, whose default value reads:</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="attr">&quot;docker_image&quot;</span>: <span class="string">&quot;archlinux/base&quot;</span>,</span><br><span class="line">    <span class="attr">&quot;docker_tag&quot;</span>: <span class="string">&quot;latest&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>All we need to do is replace the values of these two variables with the values we want, for example, I’m going to name my demo docker image “sample_image”. At the same time, we also need to add the <code>docker-btrfs</code> hook to<code>/etc/mkinitcpio.conf</code>. The following two lines in Dockerfile does the above configuration:</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">RUN</span><span class="bash"> sed -i <span class="string">&#x27;s/archlinux\/base/sample_image/g&#x27;</span> /etc/docker-btrfs.json</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> perl -i -p -e <span class="string">&#x27;s/(?&lt;=^HOOKS=\()(.*)(?=\))/$1 docker-btrfs/g&#x27;</span> /etc/mkinitcpio.conf</span></span><br></pre></td></tr></table></figure>

<p>Now we have a ready-to-use demo Dockerfile:</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> zasdfgbnm/archlinux-yaourt</span><br><span class="line"><span class="keyword">USER</span> root</span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> pacman -Syu --noconfirm base</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> sudo -u user yaourt -S --noconfirm mkinitcpio-docker-hooks</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> sed -i <span class="string">&#x27;s/archlinux\/base/sample_image/g&#x27;</span> /etc/docker-btrfs.json</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> perl -i -p -e <span class="string">&#x27;s/(?&lt;=^HOOKS=\()(.*)(?=\))/$1 docker-btrfs/g&#x27;</span> /etc/mkinitcpio.conf</span></span><br></pre></td></tr></table></figure>

<p>We can the use the command <code>docker build . -t sample_image</code> to create our docker image from this Dockerfile.</p>
<h2 id="Prepare-kernel-and-initramfs"><a href="#Prepare-kernel-and-initramfs" class="headerlink" title="Prepare kernel and initramfs"></a>Prepare kernel and initramfs</h2><p>After the image is generated, the next step is to prepare the kernel and initramfs. Note that this step is best done on the machine that you intend to use to start the docker image because mkinitcpio automatically places the appropriate kernel modules into initramfs based on the machine, and if run on other machines, there may be wrong drivers will be packed into initramfs. As mentioned earlier, this step is done inside the docker container.</p>
<p>First, run the container and open an interactive shell:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -v $(<span class="built_in">pwd</span>):/workspace -w /workspace -it sample_image bash</span><br></pre></td></tr></table></figure>
<p>Then, run the following commands inside the shell:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mkinitcpio -p linux</span><br><span class="line">cp /boot/* .</span><br><span class="line"><span class="built_in">exit</span></span><br></pre></td></tr></table></figure>
<p>Now you can see the <code>vmlinuz-linux</code> and generated <code>initramfs-linux-fallback.img</code> and <code>initramfs-linux.img</code> in the current directory.</p>
<h2 id="Prepare-the-top-layer-content"><a href="#Prepare-the-top-layer-content" class="headerlink" title="Prepare the top layer content"></a>Prepare the top layer content</h2><p>The top layer is a new concept introduced by mkinitcpio-docker-hooks. It refers to a directory in a drive that will be copied to the rwlayer via busybox’s <code>cp -a</code> at startup, before mounting the real root and after the creation of rwlayer. Why do you need top layer? Because we need to start the same image on multiple machines, different machines often need to have different configuration files, such as <code>/etc/fstab</code> and <code>/etc/X11/xorg.conf</code>. In addition, DockerHub free account can only host very few private images, but <code>/etc/passwd</code>, <code>/etc/shadow</code> and other private files are not suitable to be in a public image.</p>
<p>Preparation of the top layer is to find a folder to store separate configuration files, in a structure the same way as in image’s file system. For example, if you want a separate <code>/etc/fstab</code>, you should add the file<code>etc/fstab</code> in the top layer’s directory. Here’s the suggested operation flow: first, enter the shell in the container through <code>docker run -v $(pwd): /workspace -w /workspace -it sample_image bash</code>; then, config the system in the shell, such as <code>useradd ...</code>; finally, copy new configuration file to the top layer folder.</p>
<h2 id="Setup-boot-manager"><a href="#Setup-boot-manager" class="headerlink" title="Setup boot manager"></a>Setup boot manager</h2><p>Now the only thing we need is to set the boot manager. Here we take refind as an example. Everything (docker directory, the top layer, kernel, initramfs) is assumed to be in a btrfs partition labeled “linux”. The docker directory (i.e. the <code>/var/lib/docker</code>) is in a subvolume called “docker” at the root of this partition. The kernel, initramfs, and top layer are all located in the “boot_docker” folder at the root of the partition. We want to name the rwlayer “docker_rwlayer”. Then the menuentry in <code>refind.conf</code> should read:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">menuentry archlinux-docker &#123;</span><br><span class="line">        icon EFI&#x2F;refind&#x2F;icons&#x2F;os_arch.png</span><br><span class="line">        volume linux</span><br><span class="line">        loader boot_docker&#x2F;vmlinuz-linux</span><br><span class="line">        initrd boot_docker&#x2F;initramfs-linux.img</span><br><span class="line">        options &quot;root&#x3D;LABEL&#x3D;linux rootflags&#x3D;subvol&#x3D;docker_rwlayer rw docker_path&#x3D;docker toplayer&#x3D;LABEL&#x3D;linux toplayer_path&#x3D;boot_docker&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Among the kernel options, we specify the partition where the docker directory is located by <code>root</code>. The <code>subvol</code> inside <code>rootflags</code> is used to specify the location of the rwlayer. The <code>docker_path</code> is used to specify the relative position of the docker directory in <code>root</code>. The partition where the top layer is located is specified by <code>toplayer</code>, and <code>toplayerflags</code> is used to specify the mount option for that partition. The <code>toplayer_path</code> is used to specify the relative position of the top layer directory in the<code> toplayer</code> partition.</p>
<p>Everything is done. Reboot and enjoy!</p>
<p>In addition, interested readers can take a look at the docker image that the author is using:<br><a target="_blank" rel="noopener" href="https://cloud.docker.com/swarm/zasdfgbnmsystem/repository/docker/zasdfgbnmsystem/archlinux-kde/general">https://cloud.docker.com/swarm/zasdfgbnmsystem/repository/docker/zasdfgbnmsystem/archlinux-kde/general</a><br><a target="_blank" rel="noopener" href="https://github.com/zasdfgbnm-dockers/archlinux-kde">https://github.com/zasdfgbnm-dockers/archlinux-kde</a></p>

    </div>

    
    
    
        

  <div class="followme">
    <p>Welcome to my other publishing channels</p>

    <div class="social-list">

        <div class="social-item">
          <a target="_blank" class="social-link" href="https://twitter.com/gaoxiang_ai">
            <span class="icon">
              <i class="fab fa-twitter"></i>
            </span>

            <span class="label">Twitter</span>
          </a>
        </div>

        <div class="social-item">
          <a target="_blank" class="social-link" href="/atom.xml">
            <span class="icon">
              <i class="fa fa-rss"></i>
            </span>

            <span class="label">RSS</span>
          </a>
        </div>
    </div>
  </div>


      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/Linux/" rel="tag"># Linux</a>
              <a href="/tags/docker/" rel="tag"># docker</a>
              <a href="/tags/initramfs/" rel="tag"># initramfs</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2017/12/28/%E6%8A%8Adocker%E9%95%9C%E5%83%8F%E5%BD%93%E4%BD%9C%E6%A1%8C%E9%9D%A2%E7%B3%BB%E7%BB%9F%E6%9D%A5%E7%94%A8/" rel="prev" title="把docker镜像当作桌面系统来用">
      <i class="fa fa-chevron-left"></i> 把docker镜像当作桌面系统来用
    </a></div>
      <div class="post-nav-item">
    <a href="/2018/06/11/%E4%BB%8E%E5%A4%B4%E5%BC%80%E5%A7%8B%E9%98%85%E8%AF%BBPyTorch%E4%BB%A3%E7%A0%81%20--%20Operators%E7%AF%87/" rel="next" title="从头开始阅读PyTorch代码 -- Operators篇">
      从头开始阅读PyTorch代码 -- Operators篇 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          
    
  <div class="comments">
    <div id="disqus_thread">
      <noscript>Please enable JavaScript to view the comments powered by Disqus.</noscript>
    </div>
  </div>
  

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#The-philosophy-of-docker"><span class="nav-number">1.</span> <span class="nav-text">The philosophy of docker</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Docker-storage-driver"><span class="nav-number">2.</span> <span class="nav-text">Docker storage driver</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Boot-to-a-docker-image"><span class="nav-number">3.</span> <span class="nav-text">Boot to a docker image</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Make-a-docker-image-your-desktop-system-using-mkinitcpio-docker-hooks"><span class="nav-number">4.</span> <span class="nav-text">Make a docker image your desktop system using mkinitcpio-docker-hooks</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Prepare-a-docker-image"><span class="nav-number">4.1.</span> <span class="nav-text">Prepare a docker image</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Installation-and-configuration-of-mkinitcpio-docker-hooks"><span class="nav-number">4.2.</span> <span class="nav-text">Installation and configuration of mkinitcpio-docker-hooks</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Prepare-kernel-and-initramfs"><span class="nav-number">4.3.</span> <span class="nav-text">Prepare kernel and initramfs</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Prepare-the-top-layer-content"><span class="nav-number">4.4.</span> <span class="nav-text">Prepare the top layer content</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Setup-boot-manager"><span class="nav-number">4.5.</span> <span class="nav-text">Setup boot manager</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">zasdfgbnm</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">11</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">categories</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">56</span>
        <span class="site-state-item-name">tags</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/zasdfgbnm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;zasdfgbnm" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://twitter.com/gaoxiang_ai" title="Twitter → https:&#x2F;&#x2F;twitter.com&#x2F;gaoxiang_ai" rel="noopener" target="_blank"><i class="fab fa-twitter fa-fw"></i>Twitter</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">zasdfgbnm</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://muse.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a>
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/muse.js"></script>


<script src="/js/next-boot.js"></script>




  
  <script>
    (function(){
      var canonicalURL, curProtocol;
      //Get the <link> tag
      var x=document.getElementsByTagName("link");
		//Find the last canonical URL
		if(x.length > 0){
			for (i=0;i<x.length;i++){
				if(x[i].rel.toLowerCase() == 'canonical' && x[i].href){
					canonicalURL=x[i].href;
				}
			}
		}
    //Get protocol
	    if (!canonicalURL){
	    	curProtocol = window.location.protocol.split(':')[0];
	    }
	    else{
	    	curProtocol = canonicalURL.split(':')[0];
	    }
      //Get current URL if the canonical URL does not exist
	    if (!canonicalURL) canonicalURL = window.location.href;
	    //Assign script content. Replace current URL with the canonical URL
      !function(){var e=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,r=canonicalURL,t=document.referrer;if(!e.test(r)){var n=(String(curProtocol).toLowerCase() === 'https')?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";t?(n+="?r="+encodeURIComponent(document.referrer),r&&(n+="&l="+r)):r&&(n+="?l="+r);var i=new Image;i.src=n}}(window);})();
  </script>















  

  

  

<script>
  function loadCount() {
    var d = document, s = d.createElement('script');
    s.src = 'https://zasdfgbnm-github-io.disqus.com/count.js';
    s.id = 'dsq-count-scr';
    (d.head || d.body).appendChild(s);
  }
  // defer loading until the whole page loading is completed
  window.addEventListener('load', loadCount, false);
</script>
<script>
  var disqus_config = function() {
    this.page.url = "https://zasdfgbnm.github.io/2017/12/29/Using-docker-image-as-your-desktop-system/";
    this.page.identifier = "2017/12/29/Using-docker-image-as-your-desktop-system/";
    this.page.title = "Using docker image as your desktop system";
    };
  NexT.utils.loadComments(document.querySelector('#disqus_thread'), () => {
    if (window.DISQUS) {
      DISQUS.reset({
        reload: true,
        config: disqus_config
      });
    } else {
      var d = document, s = d.createElement('script');
      s.src = 'https://zasdfgbnm-github-io.disqus.com/embed.js';
      s.setAttribute('data-timestamp', '' + +new Date());
      (d.head || d.body).appendChild(s);
    }
  });
</script>

</body>
</html>
